#include "parser.h"

static char *token_begin[] = {
    "duration ",
    "file ",
    "tag album ",
    "tag albumartist ",
    "tag artist ",
    "tag comment ",
    "tag date ",
    "tag genre ",
    "tag title ",
    "tag discnumber ",
    "tag tracknumber ",
    "set aaa_mode ",
    "status ",
    "set vol_left ",
    "set vol_right ",
    "position ",
    "set repeat ",
    "set repeat_current ",
    "set shuffle "
};

static char *line_end(char *str) {
    char *p;
    if ((p = strchr(str, '\n')) != NULL)
        return p;
    return (p = strchr(str, '\0'));
}

static char *substr(char *begin, char *end) {
    size_t len = end - begin;
    if (len <= 0)
        return NULL;
    char *substr = malloc(len + 1);
    memset(substr, 0, len + 1);
    strncpy(substr, begin, len);
    assert(substr[len] == '\0');
    return substr;
}

static void reassign_uint64_if_changed(uint64_t *old, char *old_str) {
    /* as per documentation, reset errno for strtoul */
    errno = 0;
    uint64_t new = strtoul(old_str, NULL, 0);
    /* check for conversion errors */
    if (new == 0 && (errno == EINVAL || errno == ERANGE))
        fprintf(stderr, "error converting uint string: %s\n", old_str);
    /* only change what changed */
    if (*old != new)
        *old = new;
}

static void reassign_str_if_changed(char *new, char **old) {
    if (*old == NULL || strcmp(new, *old) != 0) {
        if (DEBUG)
            printf("str changed %s\n", new);
        free(*old);
        *old = strdup(new);
    }
}

void fill_track_or_player(track *cr_track, player *cr_player, char *buf, int buf_len, bool *file_changed, bool *incomplete_parse) {
    /* todo: more sophisticated parsing and handling */
    if (*incomplete_parse) {
        fprintf(stderr, "incomplete parse detected!\n");
        *incomplete_parse = false;
    }

    for (int i = 0; i < TOKEN_ENUM_SIZE; i++) {
        /* search for token sequence */
        char *begin_p = strstr(buf, token_begin[i]);
        if (begin_p == NULL)
            continue;
        /* skip the begin sequences itself */
        begin_p += strlen(token_begin[i]);
        /* search for end, beginning at begin_p */
        char *end_p = line_end(begin_p);

        char *part = substr(begin_p, end_p);
        if (part == NULL)
            continue;

        /* oh boy, here we go */
        switch (i) {
            case TOKEN_DURATION: {
                reassign_uint64_if_changed(&cr_track->duration, part);
                break;
            }

            case TOKEN_FILE: {
                /* special case, but kind of ugly */
                if (cr_track->file == NULL || strcmp(part, cr_track->file) != 0) {
                    if (DEBUG)
                        printf("str changed %s\n", part);
                    free(cr_track->file);
                    cr_track->file = strdup(part);
                    *file_changed = true;
                }
                break;
            }

            case TOKEN_ALBUM_TAG: {
                reassign_str_if_changed(part, &cr_track->album_tag);
                break;
            }

            case TOKEN_ALBUMARTIST_TAG: {
                reassign_str_if_changed(part, &cr_track->albumartist_tag);
                break;
            }

            case TOKEN_ARTIST_TAG: {
                reassign_str_if_changed(part, &cr_track->artist_tag);
                break;
            }

            case TOKEN_COMMENT_TAG: {
                reassign_str_if_changed(part, &cr_track->comment_tag);
                break;
            }

            case TOKEN_DATE_TAG: {
                reassign_str_if_changed(part, &cr_track->date_tag);
                break;
            }

            case TOKEN_GENRE_TAG: {
                reassign_str_if_changed(part, &cr_track->genre_tag);
                break;
            }

            case TOKEN_TITLE_TAG: {
                reassign_str_if_changed(part, &cr_track->title_tag);
                break;
            }

            case TOKEN_DISCNUMBER_TAG: {
                reassign_uint64_if_changed(&cr_track->discnumber_tag, part);
                break;
            }

            case TOKEN_TRACKNUMBER_TAG: {
                reassign_uint64_if_changed(&cr_track->tracknumber_tag, part);
                break;
            }

            case TOKEN_AAA_MODE: {
                reassign_str_if_changed(part, &cr_player->aaa_mode);
                break;
            }

            case TOKEN_STATUS: {
                reassign_str_if_changed(part, &cr_player->status);
                break;
            }

            case TOKEN_VOL_LEFT: {
                reassign_uint64_if_changed(&cr_player->vol_left, part);
                break;
            }

            case TOKEN_VOL_RIGHT: {
                reassign_uint64_if_changed(&cr_player->vol_right, part);
                break;
            }

            case TOKEN_POSITION: {
                reassign_uint64_if_changed(&cr_player->position, part);
                break;
            }

            case TOKEN_REPEAT: {
                cr_player->repeat = strcmp(part, "true") == 0;
                break;
            }

            case TOKEN_REPEAT_CURRENT: {
                cr_player->repeat_current = strcmp(part, "true") == 0;
                break;
            }

            case TOKEN_SHUFFLE: {
                cr_player->shuffle = strcmp(part, "true") == 0;
                break;
            }

            default:
                fprintf(stderr, "default case during parsing reached!\n");
                break;

            if (*end_p == '\0')
                *incomplete_parse = true;
        }

        /* free previously assigned part */
        free(part);
    }
}